package com.pazl.check.interceptor;


import java.util.Date;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.NamedThreadLocal;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import com.pazl.check.enums.ReturnCodeEnum;
import com.pazl.check.exception.BusinessException;
import com.pazl.check.utils.JacksonUtil;
import com.pazl.check.utils.MessageUtil;
import com.pazl.check.utils.StringUtil;

/**
 * 请求记录
 *
 */
@Aspect  
@Configuration
public class GlobalControllerHandler {  
	
	static final int SLOW_EXEC_TIMEOUT = 500;
	
	Logger logger = LoggerFactory.getLogger(this.getClass()); 
	
	//日志
	NamedThreadLocal<String> logBuilder = new NamedThreadLocal<>("logBuilder");

	//监控方法执行时间
	NamedThreadLocal<Long>  startTimeThreadLocal = new NamedThreadLocal<Long>("StopWatch-StartTime");

  /** 
   * @Description: 方法调用前触发  
   */  
  @Before("execution(* com.pazl.check.*.controller..*.*(..))")
  public void doBeforeInServiceLayer(JoinPoint joinPoint) {
	  startTimeThreadLocal.set(System.currentTimeMillis());
	  ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
	  HttpServletRequest request = attributes.getRequest();
      
      String log = "\n------------------------ S T A R T -------------------------" + "\n" +
				"请求时间 : " + new Date().toString() + "\n" +
				"请求地址 : " + request.getRequestURL().toString() + "\n" +
				"请求方式 : " + request.getMethod() + "\n" +
				"请求IP : " + request.getRemoteAddr() + "\n" +
				"后台方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + "\n";
      if(StringUtil.isNotEmpty(joinPoint.getArgs())){
    	  for(Object obj : joinPoint.getArgs()){
    		  if(!(obj instanceof HttpServletRequest) && !(obj instanceof HttpServletResponse)
    				  && !(obj instanceof MultipartFile)) {
				  log += "请求参数 : " + JacksonUtil.toJson(obj) + "\n";
    		  }
    	  }
      }
      logBuilder.set(log);
  }

  /** 
   * @Description: 方法调用后触发  
   */  
  @After("execution(* com.pazl.check.*.controller..*.*(..))")
  public void doAfterInServiceLayer(JoinPoint joinPoint) {
	  ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
      HttpServletRequest request = attributes.getRequest();
	  
	  long endTime = System.currentTimeMillis();
      long beginTime = startTimeThreadLocal.get();
      long consumeTime = endTime - beginTime;
      
      //处理时间超过500毫秒的请求为慢请求  
      if(consumeTime > SLOW_EXEC_TIMEOUT) {
      	logger.info("请求提醒 : 拦截到慢请求，请求为："+String.format("%s consume %d millis", request.getRequestURI(), consumeTime));
      }
  }  

  /** 
   * @Description: 环绕触发  
   */  
  @Around("execution(* com.pazl.check.*.controller..*.*(..))")
  public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
      Object result = null;
      try{
    	  result = pjp.proceed();
      }catch(BusinessException e){
    	  logger.error("请求失败," + e.getReturnCode().getValue() + ":" + e.getMessage(), e);
    	  result = MessageUtil.buildDto(e.getReturnCode(), e.getMessage(), null);
      }catch(ConstraintViolationException e){
    	  Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
          StringBuilder builder = new StringBuilder();
          for (ConstraintViolation<?> violation : violations ) {
        	  builder.append(violation.getMessage() + "\n");
          }
          result = MessageUtil.buildDto(ReturnCodeEnum.UNVALID, builder.toString());
      }catch(Exception e){
    	  logger.error("请求失败, 未捕获的异常", e);
    	  result = MessageUtil.buildDto(ReturnCodeEnum.ERROR, "请求异常!", null);
      }
      logger.info(logBuilder.get() + "请求结果 : " + JacksonUtil.toJson(result) + "\n" +
    		  	"------------------------ E N D -------------------------");
      return result;
  }
  
}